#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/compat.h>

#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>

#include <linux/uaccess.h>
#include <linux/kfifo.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/math.h>
#include <linux/timekeeping.h>
struct IMUDATA {
  int16_t accx;
  int16_t accy;
  int16_t accz;
  int16_t gyrox;
  int16_t gyroy;
  int16_t gyroz;
  int16_t magx;
  int16_t magy;
  int16_t magz;
  ktime_t timestamp;
};
#define IMUFIFO_MAX_ELEMENT 4

DECLARE_KFIFO(imu_fifo, struct IMUDATA, IMUFIFO_MAX_ELEMENT);

#define HIGH 1
#define LOW 0
typedef unsigned char uint8_t;

static struct task_struct *my_task;
static const unsigned int CSB1 = 111;
static const unsigned int CSB2 = 39;
static const unsigned int CSB3 = 40;

static const unsigned int x_lsb = 0x02;
static const unsigned int y_lsb = 0x04;
static const unsigned int z_lsb = 0x06;

static void digitalWrite(const unsigned int pin, int loworhigh) {
  int ret;
  ret = gpio_direction_output(pin, loworhigh); // 将 GPIO 引脚设置为低电平输出
  if (ret) {
    printk(KERN_ALERT "Failed to set direction of GPIO %d->%d\n", pin, loworhigh);
  } else {
    //  printk(KERN_ALERT "Successfully set direction of GPIO %d->%d\n", pin, loworhigh);
  }
}
static void
SPI_transfer(struct spi_device *dev, uint8_t data) {
  struct spi_transfer t = {
      .tx_buf = &data,
      .len = 1,
      .speed_hz = 8000000,
  };
  struct spi_message m;

  spi_message_init(&m);
  spi_message_add_tail(&t, &m);

  spi_sync(dev, &m);
}
static uint8_t
SPI_transfer_read(struct spi_device *dev) {
  uint8_t data;
  struct spi_transfer t = {
      .rx_buf = &data,
      .len = 1,
      .speed_hz = 8000000,
  };
  struct spi_message m;

  spi_message_init(&m);
  spi_message_add_tail(&t, &m);

  spi_sync(dev, &m);
  return data;
}
static void powermag(struct spi_device *dev) {
  digitalWrite(CSB3, LOW);
  SPI_transfer(dev, 0x4B);
  SPI_transfer(dev, 0x83);
  digitalWrite(CSB3, HIGH);
  mdelay(100);
  digitalWrite(CSB3, LOW);
  SPI_transfer(dev, 0x4C);
  SPI_transfer(dev, 0x38);
  digitalWrite(CSB3, HIGH);
}

static void setup(struct spi_device *dev) {
  // SPI.setBitOrder(MSBFIRST);
  // SPI.setDataMode(SPI_MODE0);
  uint8_t filtering;
  digitalWrite(CSB1, HIGH);
  digitalWrite(CSB2, HIGH);
  digitalWrite(CSB3, HIGH);

  // soft reset
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x7e); // ACC_PWR_CTRL);
  SPI_transfer(dev, 0xb6);
  digitalWrite(CSB1, HIGH);

  mdelay(50);

  // change acc to spi mode by read id
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x00 | 0x80);
  SPI_transfer_read(dev);
  filtering = SPI_transfer_read(dev);
  digitalWrite(CSB1, HIGH);
  mdelay(50);

  // power acc
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x7d); // ACC_PWR_CTRL);
  SPI_transfer(dev, 0x04);
  digitalWrite(CSB1, HIGH);
  mdelay(50);

  // read acc id
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x00 | 0x80);
  SPI_transfer_read(dev);
  filtering = SPI_transfer_read(dev);
  digitalWrite(CSB1, HIGH);
  printk(KERN_ALERT "acc id:0x%02X\n", filtering);

  // read gyro id
  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x00 | 0x80);
  filtering = SPI_transfer_read(dev);
  digitalWrite(CSB2, HIGH);
  printk(KERN_ALERT "gyro id:0x%02X\n", filtering);

  // set acc range to +-24g
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x41);
  SPI_transfer(dev, 0x03); //+-24g
  digitalWrite(CSB1, HIGH);

  // set acc bandwidth
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x40); // bw:145hz,odr:400hz
  SPI_transfer(dev, 0xaa);
  digitalWrite(CSB1, HIGH);

  // enable acc output filter
  /*digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x13 | 0x80);
  SPI_transfer_read(dev) & 0xc0;
  filtering = SPI_transfer_read(dev) & 0xc0;
  digitalWrite(CSB1, HIGH);

  filtering &= ~0x80;

  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x13);
  SPI_transfer(dev, filtering);
  digitalWrite(CSB1, HIGH);

  // enable acc register shadow
  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x13 | 0x80);
  SPI_transfer_read(dev) & 0xc0;
  filtering = SPI_transfer_read(dev) & 0xc0;
  digitalWrite(CSB1, HIGH);

  filtering &= ~0x40;

  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x13);
  SPI_transfer(dev, filtering);
  digitalWrite(CSB1, HIGH);
*/
  // set gyro range to +-1000degree/s
  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x0f);
  SPI_transfer(dev, 1);
  digitalWrite(CSB2, HIGH);

  // set gyro bandwidth to 200hz
  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x10);
  SPI_transfer(dev, 0x6);
  digitalWrite(CSB2, HIGH);

  // enable gyro output filter
  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x13 | 0x80);
  filtering = SPI_transfer_read(dev) & 0xc0;
  digitalWrite(CSB2, HIGH);

  filtering &= ~0x80;

  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x13);
  SPI_transfer(dev, filtering);
  digitalWrite(CSB2, HIGH);

  // enable gyro register shadow
  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x13 | 0x80);
  filtering = SPI_transfer_read(dev) & 0xc0;
  digitalWrite(CSB2, HIGH);

  filtering &= ~0x40;

  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, 0x13);
  SPI_transfer(dev, filtering);
  digitalWrite(CSB2, HIGH);

  // powermag(dev);
}
static int16_t readAcc(struct spi_device *dev, const unsigned int coor) {
  int16_t aux, msb;

  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x80 + coor); // prepare for read operation register coor where are LSB.
  SPI_transfer_read(dev);         // reads LSB (4 bits)
  aux = SPI_transfer_read(dev);   // reads LSB (4 bits)
  digitalWrite(CSB1, HIGH);
  // check if acceleration value has been updated since last it has been read out last
  // if (aux & 1) {
  // aux = aux >> 4;

  digitalWrite(CSB1, LOW);
  SPI_transfer(dev, 0x80 + (coor + 1)); // MSB is on the next register
  SPI_transfer_read(dev);               // sums MSB and LSB
  msb = SPI_transfer_read(dev);         // sums MSB and LSB
  printk(KERN_ALERT "accx lsb:0x%02X\n", aux);
  printk(KERN_ALERT "accx msb:0x%02X\n", msb);
  digitalWrite(CSB1, HIGH);

  // aux &= 0xf0;
  msb = msb << 8;
  aux = msb | aux;
  return aux;
  // } else
  //  return 0;
}

static int16_t readGyr(struct spi_device *dev, const unsigned int coor) {
  int16_t lsb;
  int16_t msb;
  int16_t aux;

  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, (0x80 + coor));
  lsb = SPI_transfer_read(dev);
  digitalWrite(CSB2, HIGH);

  digitalWrite(CSB2, LOW);
  SPI_transfer(dev, (0x80 + coor + 1));
  msb = SPI_transfer_read(dev);
  digitalWrite(CSB2, HIGH);

  aux = msb << 8;
  aux |= lsb;
  return aux;
}

static int16_t readMag(struct spi_device *dev, const unsigned int coor) {

  int16_t aux, msb;

  digitalWrite(CSB3, LOW);
  SPI_transfer(dev, (0x40 + coor) | 0x80);
  aux = SPI_transfer_read(dev);
  digitalWrite(CSB3, HIGH);

  digitalWrite(CSB3, LOW);
  SPI_transfer(dev, (0x40 + coor + 1) | 0x80);
  msb = SPI_transfer_read(dev);
  digitalWrite(CSB3, HIGH);
  msb <<= 8;
  aux = msb | aux;

  if (coor == 0x06)
    return aux / 2;
  else
    return aux / 8;
}
static uint8_t getmagid(struct spi_device *dev) {
  uint8_t id;
  digitalWrite(CSB3, LOW);
  SPI_transfer(dev, 0x40 | 0x80);
  id = SPI_transfer_read(dev);
  digitalWrite(CSB3, HIGH);
  return id;
}
// 自定义线程函数
static int my_thread(void *data) {
  struct spi_device *spi = (struct spi_device *)data;
  struct IMUDATA imudata;
  int ret = 0;
  printk(KERN_ALERT "Hello from the new thread!\n");

  setup(spi);

  while (!kthread_should_stop()) {
    ////////////////////////////////////////////////////////////////////////ACC READ / PRINT
    printk(KERN_ALERT "magid:0x%02X\n", getmagid(spi));
    imudata.accx = readAcc(spi, x_lsb);
    imudata.accy = readAcc(spi, y_lsb);
    imudata.accz = readAcc(spi, z_lsb);
    // printk(KERN_ALERT "acc:%d,%d,%d\n", x, y, z);
    //////////////////////////////////////////////////////////////////////////GYRO READ / PRINT

    imudata.gyrox = readGyr(spi, x_lsb); //* 3.8 / 1000.0; //*3.1415926/180.0;
    imudata.gyroy = readGyr(spi, y_lsb); //    *3.8 / 1000.0;                                 //*3.1415926/180.0;
    imudata.gyroz = readGyr(spi, z_lsb); //  *3.8 / 1000.0; //*3.1415926/180.0;
    // printk(KERN_ALERT "gyro:%d,%d,%d\n", x, y, z);

    imudata.magx = readMag(spi, x_lsb);
    imudata.magy = readMag(spi, y_lsb);
    imudata.magz = readMag(spi, z_lsb);
    // printk(KERN_ALERT "mag:%d,%d,%d\n", x, y, z);
    imudata.timestamp = ktime_get();
    // 入队
    // snprintf(imudata.data, 32, "%lld,%s", ktime_get(), "hello,world");
    ret = kfifo_put(&imu_fifo, imudata); // 注意这里的元素参数不是指针
    if (!ret) {
      printk(KERN_ALERT "kfifo_put fail, fifo is full\n");
    } else {
      // printk(KERN_ALERT "kfifo_put ok\n");
    }

    msleep(5); // 休眠100ms
  }

  return 0;
}

/*
 * This supports access to SPI devices using normal userspace I/O calls.
 * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
 * and often mask message boundaries, full SPI support requires full duplex
 * transfers.  There are several kinds of internal message boundaries to
 * handle chipselect management and other protocol options.
 *
 * SPI has a character major number assigned.  We allocate minor numbers
 * dynamically using a bitmask.  You must use hotplug tools, such as udev
 * (or mdev with busybox) to create and destroy the /dev/spidevB.C device
 * nodes, since there is no fixed association of minor numbers with any
 * particular SPI bus or device.
 */
#define SPIDEV_MAJOR 153 /* assigned */
#define N_SPI_MINORS 32  /* ... up to 256 */

static DECLARE_BITMAP(minors, N_SPI_MINORS);

static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);

/* Bit masks for spi_device.mode management.  Note that incorrect
 * settings for some settings can cause *lots* of trouble for other
 * devices on a shared bus:
 *
 *  - CS_HIGH ... this device will be active when it shouldn't be
 *  - 3WIRE ... when active, it won't behave as it should
 *  - NO_CS ... there will be no explicit message boundaries; this
 *	is completely incompatible with the shared bus model
 *  - READY ... transfers may proceed when they shouldn't.
 *
 * REVISIT should changing those flags be privileged?
 */
#define SPI_MODE_MASK (SPI_MODE_X_MASK | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP | SPI_NO_CS | SPI_READY | SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL | SPI_RX_CPHA_FLIP)

struct bmx055_data {
  dev_t devt;
  spinlock_t spi_lock;
  struct spi_device *spi;
  struct list_head device_entry;

  /* TX/RX buffers are NULL unless this device is open (users > 0) */
  struct mutex buf_lock;
  unsigned users;
  u8 *tx_buffer;
  u8 *rx_buffer;
  u32 speed_hz;
};

static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);

static unsigned bufsiz = 4096;
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");

/*-------------------------------------------------------------------------*/

static ssize_t
bmx055_sync(struct bmx055_data *spidev, struct spi_message *message) {
  int status;
  struct spi_device *spi;

  spin_lock_irq(&spidev->spi_lock);
  spi = spidev->spi;
  spin_unlock_irq(&spidev->spi_lock);

  if (spi == NULL)
    status = -ESHUTDOWN;
  else
    status = spi_sync(spi, message);

  if (status == 0)
    status = message->actual_length;

  return status;
}

static inline ssize_t
bmx055_sync_write(struct bmx055_data *spidev, size_t len) {
  struct spi_transfer t = {
      .tx_buf = spidev->tx_buffer,
      .len = len,
      .speed_hz = 2000000, // spidev->speed_hz,
  };
  struct spi_message m;

  spi_message_init(&m);
  spi_message_add_tail(&t, &m);
  return bmx055_sync(spidev, &m);
}

static inline ssize_t
bmx055_sync_read(struct bmx055_data *spidev, size_t len) {
  struct spi_transfer t = {
      .rx_buf = spidev->rx_buffer,
      .len = len,
      .speed_hz = 8000000, // spidev->speed_hz,
  };
  struct spi_message m;

  spi_message_init(&m);
  spi_message_add_tail(&t, &m);
  return bmx055_sync(spidev, &m);
}

/*-------------------------------------------------------------------------*/

/* Read-only message with current device setup */
static ssize_t
bmx055_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
  struct IMUDATA tmpimudata;
  struct bmx055_data *spidev;
  ssize_t status;
  unsigned long missing;
  int ret;

  /* chipselect only toggles at start or end of operation */
  if (count > bufsiz)
    return -EMSGSIZE;

  spidev = filp->private_data;

  mutex_lock(&spidev->buf_lock);
  /*	status = bmx055_sync_read(spidev, count);
    if (status > 0) {
      unsigned long	missing;

      missing = copy_to_user(buf, spidev->rx_buffer, status);
      if (missing == status)
        status = -EFAULT;
      else
        status = status - missing;
    }*/
  tmpimudata.accx = readAcc(spidev->spi, 0x12);
  tmpimudata.accy = readAcc(spidev->spi, 0x14);
  tmpimudata.accz = readAcc(spidev->spi, 0x16);
  // printk(KERN_ALERT "acc:%d,%d,%d\n", x, y, z);
  //////////////////////////////////////////////////////////////////////////GYRO READ / PRINT

  tmpimudata.gyrox = readGyr(spidev->spi, x_lsb); //* 3.8 / 1000.0; //*3.1415926/180.0;
  tmpimudata.gyroy = readGyr(spidev->spi, y_lsb); //    *3.8 / 1000.0;                                 //*3.1415926/180.0;
  tmpimudata.gyroz = readGyr(spidev->spi, z_lsb); //  *3.8 / 1000.0; //*3.1415926/180.0;
  // printk(KERN_ALERT "gyro:%d,%d,%d\n", x, y, z);

  tmpimudata.magx = readMag(spidev->spi, x_lsb);
  tmpimudata.magy = readMag(spidev->spi, y_lsb);
  tmpimudata.magz = readMag(spidev->spi, z_lsb);
  // printk(KERN_ALERT "mag:%d,%d,%d\n", x, y, z);
  tmpimudata.timestamp = ktime_get();
  status = sizeof(struct IMUDATA);
  missing = copy_to_user(buf, &tmpimudata, status);
  if (missing == status)
    status = -EFAULT;
  else
    status = status - missing;

  /* ret = kfifo_get(&imu_fifo, &tmpimudata); // 注意这里的元素参数不是指针
   if (ret) {
     // printk(KERN_ALERT "kfifo_get %s\n", tmpimudata.data);

     status = sizeof(struct IMUDATA);
     missing = copy_to_user(buf, &tmpimudata, status);
     if (missing == status)
       status = -EFAULT;
     else
       status = status - missing;
   } else {
     printk(KERN_ALERT "kfifo_get fail, fifo is empty\n");
     status = -EFAULT;
   }*/
  mutex_unlock(&spidev->buf_lock);

  return status;
}

/* Write-only message with current device setup */
static ssize_t
bmx055_write(struct file *filp, const char __user *buf,
             size_t count, loff_t *f_pos) {
  struct bmx055_data *spidev;
  ssize_t status;
  unsigned long missing;

  /* chipselect only toggles at start or end of operation */
  if (count > bufsiz)
    return -EMSGSIZE;

  spidev = filp->private_data;

  mutex_lock(&spidev->buf_lock);
  missing = copy_from_user(spidev->tx_buffer, buf, count);
  if (missing == 0)
    status = bmx055_sync_write(spidev, count);
  else
    status = -EFAULT;
  mutex_unlock(&spidev->buf_lock);

  return status;
}

static int bmx055_message(struct bmx055_data *spidev,
                          struct spi_ioc_transfer *u_xfers, unsigned n_xfers) {
  struct spi_message msg;
  struct spi_transfer *k_xfers;
  struct spi_transfer *k_tmp;
  struct spi_ioc_transfer *u_tmp;
  unsigned n, total, tx_total, rx_total;
  u8 *tx_buf, *rx_buf;
  int status = -EFAULT;

  spi_message_init(&msg);
  k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
  if (k_xfers == NULL)
    return -ENOMEM;

  /* Construct spi_message, copying any tx data to bounce buffer.
   * We walk the array of user-provided transfers, using each one
   * to initialize a kernel version of the same transfer.
   */
  tx_buf = spidev->tx_buffer;
  rx_buf = spidev->rx_buffer;
  total = 0;
  tx_total = 0;
  rx_total = 0;
  for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
       n;
       n--, k_tmp++, u_tmp++) {
    /* Ensure that also following allocations from rx_buf/tx_buf will meet
     * DMA alignment requirements.
     */
    unsigned int len_aligned = ALIGN(u_tmp->len, ARCH_KMALLOC_MINALIGN);

    k_tmp->len = u_tmp->len;

    total += k_tmp->len;
    /* Since the function returns the total length of transfers
     * on success, restrict the total to positive int values to
     * avoid the return value looking like an error.  Also check
     * each transfer length to avoid arithmetic overflow.
     */
    if (total > INT_MAX || k_tmp->len > INT_MAX) {
      status = -EMSGSIZE;
      goto done;
    }

    if (u_tmp->rx_buf) {
      /* this transfer needs space in RX bounce buffer */
      rx_total += len_aligned;
      if (rx_total > bufsiz) {
        status = -EMSGSIZE;
        goto done;
      }
      k_tmp->rx_buf = rx_buf;
      rx_buf += len_aligned;
    }
    if (u_tmp->tx_buf) {
      /* this transfer needs space in TX bounce buffer */
      tx_total += len_aligned;
      if (tx_total > bufsiz) {
        status = -EMSGSIZE;
        goto done;
      }
      k_tmp->tx_buf = tx_buf;
      if (copy_from_user(tx_buf, (const u8 __user *)(uintptr_t)u_tmp->tx_buf,
                         u_tmp->len))
        goto done;
      tx_buf += len_aligned;
    }

    k_tmp->cs_change = !!u_tmp->cs_change;
    k_tmp->tx_nbits = u_tmp->tx_nbits;
    k_tmp->rx_nbits = u_tmp->rx_nbits;
    k_tmp->bits_per_word = u_tmp->bits_per_word;
    k_tmp->delay.value = u_tmp->delay_usecs;
    k_tmp->delay.unit = SPI_DELAY_UNIT_USECS;
    k_tmp->speed_hz = u_tmp->speed_hz;
    k_tmp->word_delay.value = u_tmp->word_delay_usecs;
    k_tmp->word_delay.unit = SPI_DELAY_UNIT_USECS;
    if (!k_tmp->speed_hz)
      k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE
    dev_dbg(&spidev->spi->dev,
            "  xfer len %u %s%s%s%dbits %u usec %u usec %uHz\n",
            k_tmp->len,
            k_tmp->rx_buf ? "rx " : "",
            k_tmp->tx_buf ? "tx " : "",
            k_tmp->cs_change ? "cs " : "",
            k_tmp->bits_per_word ?: spidev->spi->bits_per_word,
            k_tmp->delay.value,
            k_tmp->word_delay.value,
            k_tmp->speed_hz ?: spidev->spi->max_speed_hz);
#endif
    spi_message_add_tail(k_tmp, &msg);
  }

  status = bmx055_sync(spidev, &msg);
  if (status < 0)
    goto done;

  /* copy any rx data out of bounce buffer */
  for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
       n;
       n--, k_tmp++, u_tmp++) {
    if (u_tmp->rx_buf) {
      if (copy_to_user((u8 __user *)(uintptr_t)u_tmp->rx_buf, k_tmp->rx_buf,
                       u_tmp->len)) {
        status = -EFAULT;
        goto done;
      }
    }
  }
  status = total;

done:
  kfree(k_xfers);
  return status;
}

static struct spi_ioc_transfer *
bmx055_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
                       unsigned *n_ioc) {
  u32 tmp;

  /* Check type, command number and direction */
  if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) || _IOC_DIR(cmd) != _IOC_WRITE)
    return ERR_PTR(-ENOTTY);

  tmp = _IOC_SIZE(cmd);
  if ((tmp % sizeof(struct spi_ioc_transfer)) != 0)
    return ERR_PTR(-EINVAL);
  *n_ioc = tmp / sizeof(struct spi_ioc_transfer);
  if (*n_ioc == 0)
    return NULL;

  /* copy into scratch area */
  return memdup_user(u_ioc, tmp);
}

static long
bmx055_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
  int retval = 0;
  struct bmx055_data *spidev;
  struct spi_device *spi;
  u32 tmp;
  unsigned n_ioc;
  struct spi_ioc_transfer *ioc;

  /* Check type and command number */
  if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
    return -ENOTTY;

  /* guard against device removal before, or while,
   * we issue this ioctl.
   */
  spidev = filp->private_data;
  spin_lock_irq(&spidev->spi_lock);
  spi = spi_dev_get(spidev->spi);
  spin_unlock_irq(&spidev->spi_lock);

  if (spi == NULL)
    return -ESHUTDOWN;

  /* use the buffer lock here for triple duty:
   *  - prevent I/O (from us) so calling spi_setup() is safe;
   *  - prevent concurrent SPI_IOC_WR_* from morphing
   *    data fields while SPI_IOC_RD_* reads them;
   *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
   */
  mutex_lock(&spidev->buf_lock);

  switch (cmd) {
  /* read requests */
  case SPI_IOC_RD_MODE:
  case SPI_IOC_RD_MODE32:
    tmp = spi->mode;

    {
      struct spi_controller *ctlr = spi->controller;

      if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
          ctlr->cs_gpiods[spi->chip_select])
        tmp &= ~SPI_CS_HIGH;
    }

    if (cmd == SPI_IOC_RD_MODE)
      retval = put_user(tmp & SPI_MODE_MASK,
                        (__u8 __user *)arg);
    else
      retval = put_user(tmp & SPI_MODE_MASK,
                        (__u32 __user *)arg);
    break;
  case SPI_IOC_RD_LSB_FIRST:
    retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
                      (__u8 __user *)arg);
    break;
  case SPI_IOC_RD_BITS_PER_WORD:
    retval = put_user(spi->bits_per_word, (__u8 __user *)arg);
    break;
  case SPI_IOC_RD_MAX_SPEED_HZ:
    retval = put_user(spidev->speed_hz, (__u32 __user *)arg);
    break;

  /* write requests */
  case SPI_IOC_WR_MODE:
  case SPI_IOC_WR_MODE32:
    if (cmd == SPI_IOC_WR_MODE)
      retval = get_user(tmp, (u8 __user *)arg);
    else
      retval = get_user(tmp, (u32 __user *)arg);
    if (retval == 0) {
      struct spi_controller *ctlr = spi->controller;
      u32 save = spi->mode;

      if (tmp & ~SPI_MODE_MASK) {
        retval = -EINVAL;
        break;
      }

      if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
          ctlr->cs_gpiods[spi->chip_select])
        tmp |= SPI_CS_HIGH;

      tmp |= spi->mode & ~SPI_MODE_MASK;
      spi->mode = tmp & SPI_MODE_USER_MASK;
      retval = spi_setup(spi);
      if (retval < 0)
        spi->mode = save;
      else
        dev_dbg(&spi->dev, "spi mode %x\n", tmp);
    }
    break;
  case SPI_IOC_WR_LSB_FIRST:
    retval = get_user(tmp, (__u8 __user *)arg);
    if (retval == 0) {
      u32 save = spi->mode;

      if (tmp)
        spi->mode |= SPI_LSB_FIRST;
      else
        spi->mode &= ~SPI_LSB_FIRST;
      retval = spi_setup(spi);
      if (retval < 0)
        spi->mode = save;
      else
        dev_dbg(&spi->dev, "%csb first\n",
                tmp ? 'l' : 'm');
    }
    break;
  case SPI_IOC_WR_BITS_PER_WORD:
    retval = get_user(tmp, (__u8 __user *)arg);
    if (retval == 0) {
      u8 save = spi->bits_per_word;

      spi->bits_per_word = tmp;
      retval = spi_setup(spi);
      if (retval < 0)
        spi->bits_per_word = save;
      else
        dev_dbg(&spi->dev, "%d bits per word\n", tmp);
    }
    break;
  case SPI_IOC_WR_MAX_SPEED_HZ: {
    u32 save;

    retval = get_user(tmp, (__u32 __user *)arg);
    if (retval)
      break;
    if (tmp == 0) {
      retval = -EINVAL;
      break;
    }

    save = spi->max_speed_hz;

    spi->max_speed_hz = tmp;
    retval = spi_setup(spi);
    if (retval == 0) {
      spidev->speed_hz = tmp;
      dev_dbg(&spi->dev, "%d Hz (max)\n", spidev->speed_hz);
    }

    spi->max_speed_hz = save;
    break;
  }
  default:
    /* segmented and/or full-duplex I/O request */
    /* Check message and copy into scratch area */
    ioc = bmx055_get_ioc_message(cmd,
                                 (struct spi_ioc_transfer __user *)arg, &n_ioc);
    if (IS_ERR(ioc)) {
      retval = PTR_ERR(ioc);
      break;
    }
    if (!ioc)
      break; /* n_ioc is also 0 */

    /* translate to spi_message, execute */
    retval = bmx055_message(spidev, ioc, n_ioc);
    kfree(ioc);
    break;
  }

  mutex_unlock(&spidev->buf_lock);
  spi_dev_put(spi);
  return retval;
}

#ifdef CONFIG_COMPAT
static long
bmx055_compat_ioc_message(struct file *filp, unsigned int cmd,
                          unsigned long arg) {
  struct spi_ioc_transfer __user *u_ioc;
  int retval = 0;
  struct bmx055_data *spidev;
  struct spi_device *spi;
  unsigned n_ioc, n;
  struct spi_ioc_transfer *ioc;

  u_ioc = (struct spi_ioc_transfer __user *)compat_ptr(arg);

  /* guard against device removal before, or while,
   * we issue this ioctl.
   */
  spidev = filp->private_data;
  spin_lock_irq(&spidev->spi_lock);
  spi = spi_dev_get(spidev->spi);
  spin_unlock_irq(&spidev->spi_lock);

  if (spi == NULL)
    return -ESHUTDOWN;

  /* SPI_IOC_MESSAGE needs the buffer locked "normally" */
  mutex_lock(&spidev->buf_lock);

  /* Check message and copy into scratch area */
  ioc = bmx055_get_ioc_message(cmd, u_ioc, &n_ioc);
  if (IS_ERR(ioc)) {
    retval = PTR_ERR(ioc);
    goto done;
  }
  if (!ioc)
    goto done; /* n_ioc is also 0 */

  /* Convert buffer pointers */
  for (n = 0; n < n_ioc; n++) {
    ioc[n].rx_buf = (uintptr_t)compat_ptr(ioc[n].rx_buf);
    ioc[n].tx_buf = (uintptr_t)compat_ptr(ioc[n].tx_buf);
  }

  /* translate to spi_message, execute */
  retval = bmx055_message(spidev, ioc, n_ioc);
  kfree(ioc);

done:
  mutex_unlock(&spidev->buf_lock);
  spi_dev_put(spi);
  return retval;
}

static long
bmx055_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
  if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) && _IOC_DIR(cmd) == _IOC_WRITE)
    return bmx055_compat_ioc_message(filp, cmd, arg);

  return bmx055_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define bmx055_compat_ioctl NULL
#endif /* CONFIG_COMPAT */

static int bmx055_open(struct inode *inode, struct file *filp) {
  struct bmx055_data *spidev = NULL, *iter;
  int status = -ENXIO;

  mutex_lock(&device_list_lock);

  list_for_each_entry(iter, &device_list, device_entry) {
    if (iter->devt == inode->i_rdev) {
      status = 0;
      spidev = iter;
      break;
    }
  }

  if (!spidev) {
    pr_debug("spidev: nothing for minor %d\n", iminor(inode));
    goto err_find_dev;
  }

  if (!spidev->tx_buffer) {
    spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
    if (!spidev->tx_buffer) {
      status = -ENOMEM;
      goto err_find_dev;
    }
  }

  if (!spidev->rx_buffer) {
    spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
    if (!spidev->rx_buffer) {
      status = -ENOMEM;
      goto err_alloc_rx_buf;
    }
  }

  spidev->users++;
  filp->private_data = spidev;
  stream_open(inode, filp);

  mutex_unlock(&device_list_lock);
  setup(spidev->spi);
  // 创建并运行新线程
  /*my_task = kthread_run(&my_thread, spidev->spi, "my_thread");

  if (IS_ERR(my_task)) {
    printk(KERN_ALERT "Failed to create kernel thread.\n");
    goto err_find_dev;
  } else {
    printk(KERN_ALERT "Kernel thread created successfully.\n");
  }*/

  printk(KERN_ALERT "open bmx055 ok!\n");
  return 0;

err_alloc_rx_buf:
  kfree(spidev->tx_buffer);
  spidev->tx_buffer = NULL;
err_find_dev:
  mutex_unlock(&device_list_lock);
  return status;
}

static int bmx055_release(struct inode *inode, struct file *filp) {
  struct bmx055_data *spidev;
  int dofree;
  printk(KERN_ALERT "will release!!!\n");

  // 停止并等待线程完成
  // kthread_stop(my_task);
  printk(KERN_ALERT "has stop thread!!!\n");

  mutex_lock(&device_list_lock);
  spidev = filp->private_data;
  filp->private_data = NULL;

  spin_lock_irq(&spidev->spi_lock);
  /* ... after we unbound from the underlying device? */
  dofree = (spidev->spi == NULL);
  spin_unlock_irq(&spidev->spi_lock);

  /* last close? */
  spidev->users--;
  if (!spidev->users) {

    kfree(spidev->tx_buffer);
    spidev->tx_buffer = NULL;

    kfree(spidev->rx_buffer);
    spidev->rx_buffer = NULL;

    if (dofree)
      kfree(spidev);
    else
      spidev->speed_hz = spidev->spi->max_speed_hz;
  }
#ifdef CONFIG_SPI_SLAVE
  if (!dofree)
    spi_slave_abort(spidev->spi);
#endif
  mutex_unlock(&device_list_lock);

  return 0;
}

static const struct file_operations bmx055_fops = {
    .owner = THIS_MODULE,
    /* REVISIT switch to aio primitives, so that userspace
     * gets more complete API coverage.  It'll simplify things
     * too, except for the locking.
     */
    .write = bmx055_write,
    .read = bmx055_read,
    .unlocked_ioctl = bmx055_ioctl,
    .compat_ioctl = bmx055_compat_ioctl,
    .open = bmx055_open,
    .release = bmx055_release,
    .llseek = no_llseek,
};

/*-------------------------------------------------------------------------*/

/* The main reason to have this class is to make mdev/udev create the
 * /dev/spidevB.C character device nodes exposing our userspace API.
 * It also simplifies memory management.
 */

static struct class *bmx055_class;

static const struct spi_device_id bmx055_spi_ids[] = {
    {.name = "dh2228fv"},
    {.name = "ltc2488"},
    {.name = "sx1301"},
    {.name = "bk4"},
    {.name = "bmx055"},
    {.name = "dhcom-board"},
    {.name = "m53cpld"},
    {.name = "spi-petra"},
    {.name = "spi-authenta"},
    {},
};
MODULE_DEVICE_TABLE(spi, bmx055_spi_ids);

/*
 * spidev should never be referenced in DT without a specific compatible string,
 * it is a Linux implementation thing rather than a description of the hardware.
 */
static int bmx055_of_check(struct device *dev) {
  if (device_property_match_string(dev, "compatible", "bmx055") < 0)
    return 0;

  dev_err(dev, "spidev listed directly in DT is not supported\n");
  return -EINVAL;
}

static const struct of_device_id bmx055_dt_ids[] = {
    {.compatible = "rohm,dh2228fv", .data = &bmx055_of_check},
    {.compatible = "lineartechnology,ltc2488", .data = &bmx055_of_check},
    {.compatible = "semtech,sx1301", .data = &bmx055_of_check},
    {.compatible = "lwn,bk4", .data = &bmx055_of_check},
    {.compatible = "dh,dhcom-board", .data = &bmx055_of_check},
    {.compatible = "menlo,m53cpld", .data = &bmx055_of_check},
    {.compatible = "cisco,spi-petra", .data = &bmx055_of_check},
    {.compatible = "micron,spi-authenta", .data = &bmx055_of_check},
    {.compatible = "rockchip,bmx055", .data = &bmx055_of_check},
    {},
};
MODULE_DEVICE_TABLE(of, bmx055_dt_ids);

/* Dummy SPI devices not to be used in production systems */
static int bmx055_acpi_check(struct device *dev) {
  dev_warn(dev, "do not use this driver in production systems!\n");
  return 0;
}

static const struct acpi_device_id bmx055_acpi_ids[] = {
    /*
     * The ACPI SPT000* devices are only meant for development and
     * testing. Systems used in production should have a proper ACPI
     * description of the connected peripheral and they should also use
     * a proper driver instead of poking directly to the SPI bus.
     */
    {"SPT0001", (kernel_ulong_t)&bmx055_acpi_check},
    {"SPT0002", (kernel_ulong_t)&bmx055_acpi_check},
    {"SPT0003", (kernel_ulong_t)&bmx055_acpi_check},
    {},
};
MODULE_DEVICE_TABLE(acpi, bmx055_acpi_ids);

/*-------------------------------------------------------------------------*/

static int bmx055_probe(struct spi_device *spi) {
  int (*match)(struct device *dev);
  struct bmx055_data *spidev;
  int status;
  unsigned long minor;

  match = device_get_match_data(&spi->dev);
  if (match) {
    status = match(&spi->dev);
    if (status)
      return status;
  }

  /* Allocate driver data */
  spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
  if (!spidev)
    return -ENOMEM;

  /* Initialize the driver data */
  spidev->spi = spi;
  spin_lock_init(&spidev->spi_lock);
  mutex_init(&spidev->buf_lock);

  INIT_LIST_HEAD(&spidev->device_entry);

  /* If we can allocate a minor number, hook up this device.
   * Reusing minors is fine so long as udev or mdev is working.
   */
  mutex_lock(&device_list_lock);
  minor = find_first_zero_bit(minors, N_SPI_MINORS);
  if (minor < N_SPI_MINORS) {
    struct device *dev;

    spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
    dev = device_create(bmx055_class, &spi->dev, spidev->devt,
                        spidev, "spidev%d.%d",
                        spi->master->bus_num, spi->chip_select);
    status = PTR_ERR_OR_ZERO(dev);
  } else {
    dev_dbg(&spi->dev, "no minor number available!\n");
    status = -ENODEV;
  }
  if (status == 0) {
    set_bit(minor, minors);
    list_add(&spidev->device_entry, &device_list);
  }
  mutex_unlock(&device_list_lock);

  spidev->speed_hz = spi->max_speed_hz;

  if (status == 0)
    spi_set_drvdata(spi, spidev);
  else
    kfree(spidev);

  return status;
}

static void bmx055_remove(struct spi_device *spi) {
  struct bmx055_data *spidev = spi_get_drvdata(spi);

  /* prevent new opens */
  mutex_lock(&device_list_lock);
  /* make sure ops on existing fds can abort cleanly */
  spin_lock_irq(&spidev->spi_lock);
  spidev->spi = NULL;
  spin_unlock_irq(&spidev->spi_lock);

  list_del(&spidev->device_entry);
  device_destroy(bmx055_class, spidev->devt);
  clear_bit(MINOR(spidev->devt), minors);
  if (spidev->users == 0)
    kfree(spidev);
  mutex_unlock(&device_list_lock);
}

static struct spi_driver bmx055_spi_driver = {
    .driver = {
        .name = "bmx055",
        .of_match_table = bmx055_dt_ids,
        .acpi_match_table = bmx055_acpi_ids,
    },
    .probe = bmx055_probe,
    .remove = bmx055_remove,
    .id_table = bmx055_spi_ids,

    /* NOTE:  suspend/resume methods are not necessary here.
     * We don't do anything except pass the requests to/from
     * the underlying controller.  The refrigerator handles
     * most issues; the controller driver handles the rest.
     */
};

/*-------------------------------------------------------------------------*/

static int __init bmx055_init(void) {
  int status;

  /* Claim our 256 reserved device numbers.  Then register a class
   * that will key udev/mdev to add/remove /dev nodes.  Last, register
   * the driver which manages those device numbers.
   */
  printk(KERN_ALERT "will register char dev!\n");
  status = register_chrdev(SPIDEV_MAJOR, "spi", &bmx055_fops);
  if (status < 0)
    return status;
  printk(KERN_ALERT "after register char dev!\n");

  bmx055_class = class_create(THIS_MODULE, "bmx055");
  if (IS_ERR(bmx055_class)) {
    unregister_chrdev(SPIDEV_MAJOR, bmx055_spi_driver.driver.name);
    return PTR_ERR(bmx055_class);
  }
  printk(KERN_ALERT "after create class imuspidev!\n");

  status = spi_register_driver(&bmx055_spi_driver);
  if (status < 0) {
    class_destroy(bmx055_class);
    unregister_chrdev(SPIDEV_MAJOR, bmx055_spi_driver.driver.name);
  }
  printk(KERN_ALERT "after register driver!\n");
  return status;
}
module_init(bmx055_init);

static void __exit bmx055_exit(void) {
  spi_unregister_driver(&bmx055_spi_driver);
  class_destroy(bmx055_class);
  unregister_chrdev(SPIDEV_MAJOR, bmx055_spi_driver.driver.name);
}
module_exit(bmx055_exit);

MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:bmx055");
